
use std::ops::{Add, Sub};

use chrono;
use chrono::Timelike;
use crate::IResult;

/// author: hzsong123@126.com, 2023-0218
/// 精确到毫秒的时间,
/// 1天：1000毫秒 * 60秒 * 60分 * 24小时 = 864_00_000毫秒,
/// 最小0表示：0:0:0-0，最大863_99_999表示：23:59:59-999
/// 2022-1229, hzsong
#[derive(serde::Deserialize, serde::Serialize, Clone, Copy, PartialEq, Eq)]
pub struct TimeInt {
    /// represent the number of ticks map to the time of the day.
    ticks: i32,
}

impl TimeInt {
    pub fn new(ticks: i32) -> Self {
        if ticks < TimeI32Const::MinTicks {
            return Self::min();
        }
        if ticks > TimeI32Const::MaxTicks {
            return Self::max();
        }
        TimeInt { ticks }
    }
    pub fn now() -> Self {
        let now = chrono::prelude::Local::now();
        let millisec = now.nanosecond() as i32 / 1000_000;
        let ticks = now.hour() as i32 * TimeI32Const::TicksPerHour
            + now.minute() as i32 * TimeI32Const::TicksPerMinute
            + now.second() as i32 * TimeI32Const::TicksPerSecond
            + millisec;

        Self { ticks }
    }

    pub fn fromHmsS(hour: i32, minute: i32, second: i32, millisec: i32) -> IResult<Self> {
        if hour > 23 {
            return Err(format!("TimeInt::Ctor: Hour {} is not in [0 ~ 23]", hour))?;
        }
        if minute > 59 {
            return Err(format!(
                "TimeInt::Ctor: Minute {} is not in [0 ~ 59]",
                minute
            ))?;
        }
        if second > 59 {
            return Err(format!(
                "TimeInt::Ctor: Second {} is not in [0 ~ 59]",
                second
            ))?;
        }
        if millisec > 999 {
            return Err(format!(
                "TimeInt::Ctor: Millisec {} is not in [0 ~ 999]",
                millisec
            ))?;
        }
        let ticks = hour * TimeI32Const::TicksPerHour
            + minute * TimeI32Const::TicksPerMinute
            + second * TimeI32Const::TicksPerSecond
            + millisec;
        Ok(TimeInt { ticks })
    }
    /* pub fn fromHms(hour: i32, minute: i32, second: i32) -> IResult<Self> {
        if hour > 23 {
            return Err(format!("TimeInt::Ctor: Hour {} is not in [0 ~ 23]", hour))?;
        }
        if minute > 59 {
            return Err(format!(
                "TimeInt::Ctor: Minute {} is not in [0 ~ 59]",
                minute
            ))?;
        }
        if second > 59 {
            return Err(format!(
                "TimeInt::Ctor: Second {} is not in [0 ~ 59]",
                second
            ))?;
        }
        let ticks = hour * TimeI32Const::TicksPerHour
            + minute * TimeI32Const::TicksPerMinute
            + second * TimeI32Const::TicksPerSecond;
        Ok(TimeInt { ticks })
    } */
    /* pub fn fromHm(hour: i32, minute: i32) -> IResult<Self> {
        if hour > 23 {
            return Err(format!("TimeInt::Ctor: Hour {} is not in [0 ~ 23]", hour))?;
        }
        if minute > 59 {
            return Err(format!(
                "TimeInt::Ctor: Minute {} is not in [0 ~ 59]",
                minute
            ))?;
        }
        let ticks = hour * TimeI32Const::TicksPerHour + minute * TimeI32Const::TicksPerMinute;
        Ok(TimeInt { ticks })
    } */

    /// 9_01_02_030 -> 9:01:02:
    pub fn fromInt9(time9: i32) -> IResult<Self> {
        let hour = time9 / 1_00_00_000;
        let mm_ss_SSS = time9 % 1_00_00_000;
        let minute = mm_ss_SSS / 100_000;
        let ss_SSS = mm_ss_SSS % 100_000;
        return TimeInt::fromHmsS(hour, minute, ss_SSS / 1000, ss_SSS % 1000);

        // let mut ti = 10_00_00_00;
        // let mut com_time = |t| {
        //     let r = time9 / ti;
        //     time9 %= ti;
        //     ti /= t;
        //     // println!("r: {r}: time9: {time9}, ti: {ti}");
        //     r
        // };

        // let hour = com_time(100);

        // let minute = com_time(100);
        // let sec = com_time(100);
        // let millisec = time9;

        // return TimeI32::fromHmsS(hour, minute, sec, millisec);
    }
    pub fn from_string9_safe(time9: &str) -> Self {
        if time9.len() != 9 {
            error!("Error time_int string: {}", time9);
            return TimeInt::min();
        }

        let i_time9 = match time9.parse::<i32>() {
            Ok(v) => v,
            Err(_) => {
                error!("Error time_int string: {}", time9);
                return TimeInt::min();
            },
        };

        Self::fromInt9(i_time9).unwrap_or_else(|_| Default::default())


    }

    pub fn fromInt9_safe(time9: i32) -> Self {
        Self::fromInt9(time9).unwrap_or_else(|_| Default::default())
    }
    pub fn fromLong17_safe(date_time: i64) -> Self {
        let tm9 = date_time % 10_0000_0000;
        let me = Self::fromInt9(tm9 as i32).unwrap_or_else(|_| Default::default());

        me
    }
}

impl Add for TimeInt {
    type Output = Self;
    fn add(self, other: Self) -> Self {
        let ticks = self.ticks + other.ticks;
        Self::new(ticks)
    }
}
    
impl Sub for TimeInt {
    type Output = Self;
    fn sub(self, other: Self) -> Self {
        let ticks = self.ticks - other.ticks;
        Self::new(ticks)
    }
}



impl std::default::Default for TimeInt {
    fn default() -> Self {
        TimeInt::min()
    }
}

impl std::fmt::Display for TimeInt {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        write!(
            f,
            "{:0>2}:{:0>2}:{:0>2}-{:0>3}",
            self.hour(),
            self.minute(),
            self.second(),
            self.millisecond()
        )
    }
}
impl std::fmt::Debug for TimeInt {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        f.debug_struct("TimeInt").field("ticks", &self.ticks).field("friendly", &self.to_string9b()).finish()
    }
}



pub mod TimeI32Const {
    /// 1000
    pub const TicksPerSecond: i32 = 1000;
    /// 60_000
    pub const TicksPerMinute: i32 = TicksPerSecond * 60;
    /// 3600_000
    pub const TicksPerHour: i32 = TicksPerMinute * 60;
    /// 86400_000
    pub const TicksPerDay: i32 = TicksPerHour * 24;
    /// 0, 0:0:0-000，
    /// MinTimeTicks is the ticks for the midnight time 00:00:00.000 AM
    pub const MinTicks: i32 = 0;
    /// 86_399_999, 23:59:59-999，
    /// MaxTimeTicks is the max tick value for the time in the day. It is calculated using DateTime.Today.AddTicks(-1).TimeOfDay._ticks.
    pub const MaxTicks: i32 = 86_399_999;
}

// constant ===============================================
impl TimeInt {
    pub fn min() -> Self {
        TimeInt {
            ticks: TimeI32Const::MinTicks,
        }
    }
    pub fn max() -> Self {
        TimeInt {
            ticks: TimeI32Const::MaxTicks,
        }
    }
}

impl TimeInt {
    pub fn ticks(&self) -> i32 {
        self.ticks
    }
    pub fn hour(&self) -> i32 {
        self.ticks / TimeI32Const::TicksPerHour
    }
    pub fn minute(&self) -> i32 {
        (self.ticks / TimeI32Const::TicksPerMinute) % 60
    }
    pub fn second(&self) -> i32 {
        (self.ticks / TimeI32Const::TicksPerSecond) % 60
    }
    pub fn millisecond(&self) -> i32 {
        self.ticks % TimeI32Const::TicksPerSecond
    }
    /// 测试环境的数据回放，会有差值是负的情况，所以返回i32, 而非I32
    pub fn milliseconds_from(&self, tm_before: &Self) -> i32 {
        self.ticks as i32 - tm_before.ticks as i32
    }
    
    pub fn seconds_from(&self, tm_before: &Self) -> f64 {
        (self.ticks - tm_before.ticks) as f64 / 1000_f64
    }
    // pub fn minutes_from(&self, tm_before: &Self) -> f64 {
    //     (self.ticks - tm_before.ticks) as f64 / 60_000_f64
    // }

    pub fn to_int4(&self) -> i32 {
        let hour = self.ticks / TimeI32Const::TicksPerHour;
        let minute = (self.ticks / TimeI32Const::TicksPerMinute) % 60;
        hour * 100 + minute
    }

    pub fn to_int6(&self) -> i32 {
        let hour = self.ticks / TimeI32Const::TicksPerHour;
        let minute = (self.ticks / TimeI32Const::TicksPerMinute) % 60;
        let second = (self.ticks / TimeI32Const::TicksPerSecond) % 60;

        hour * 100_00 + minute * 100 + second
    }
    pub fn to_int9(&self) -> i32 {
        let hour = self.ticks / TimeI32Const::TicksPerHour;
        let minute = (self.ticks / TimeI32Const::TicksPerMinute) % 60;
        let second = (self.ticks / TimeI32Const::TicksPerSecond) % 60;
        let millisec = self.ticks % TimeI32Const::TicksPerSecond;
        hour * 100_00_000 + minute * 100_000 + second * 1000 + millisec
    }

    /* 
    pub fn toString4(&self) -> String {
        let hour = self.ticks / TimeI32Const::TicksPerHour;
        let minute = (self.ticks / TimeI32Const::TicksPerMinute) % 60;
        format!("{:02}{:02}", hour, minute)
    }
     */

    
    pub fn to_string6(&self) -> String {
        let hour = self.ticks / TimeI32Const::TicksPerHour;
        let minute = (self.ticks / TimeI32Const::TicksPerMinute) % 60;
        let second = (self.ticks / TimeI32Const::TicksPerSecond) % 60;

        format!("{:02}{:02}{:02}", hour, minute, second)
    }
    pub fn to_string6b(&self) -> String {
        let hour = self.ticks / TimeI32Const::TicksPerHour;
        let minute = (self.ticks / TimeI32Const::TicksPerMinute) % 60;
        let second = (self.ticks / TimeI32Const::TicksPerSecond) % 60;

        format!("{:02}:{:02}:{:02}", hour, minute, second)
    }
    pub fn to_string9(&self) -> String {
        let hour = self.ticks / TimeI32Const::TicksPerHour;
        let minute = (self.ticks / TimeI32Const::TicksPerMinute) % 60;
        let second = (self.ticks / TimeI32Const::TicksPerSecond) % 60;
        let millisec = self.ticks % TimeI32Const::TicksPerSecond;

        format!("{:02}{:02}{:02}{:03}", hour, minute, second, millisec)
    }
    
    pub fn to_string9b(&self) -> String {
        let hour = self.ticks / TimeI32Const::TicksPerHour;
        let minute = (self.ticks / TimeI32Const::TicksPerMinute) % 60;
        let second = (self.ticks / TimeI32Const::TicksPerSecond) % 60;
        let millisec = self.ticks % TimeI32Const::TicksPerSecond;

        format!("{:02}:{:02}:{:02}-{:03}", hour, minute, second, millisec)
    }
}

#[test]
fn test_to_int9() {
    let now = TimeInt::now();
    let now = now.to_int9();
    println!("now: {now}");

    // let now2 = crate::utils::time::get_now_time_9();
    // println!("now2: {now2}");
}


#[test]
fn test_from_int9() {
    let r = TimeInt::fromInt9(1_23_45_678);
    println!("{:?}", r);

    let r = TimeInt::fromInt9(11_23_45_678);
    println!("{:?}", r);

    let r = TimeInt::fromInt9(1_02_03_004);
    println!("{:?}", r);

    let r = TimeInt::fromInt9(11_02_03_004);
    println!("{:?}", r);

    let r = TimeInt::fromInt9(12_02_03_004);
    println!("{:?}", r);

    let r = TimeInt::fromInt9(00_01_02_003);
    println!("{:?}", r);

    let r = TimeInt::fromInt9(00_11_22_333);
    println!("{:?}", r);
}